package com.chuanke.ckfamily.service.finance.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chuanke.ckfamily.dao.base.CkUtil;
import com.chuanke.ckfamily.dao.finance.GainPacketDao;
import com.chuanke.ckfamily.dao.finance.RedPacketDao;
import com.chuanke.ckfamily.dao.finance.model.Bill;
import com.chuanke.ckfamily.dao.finance.model.GainPacket;
import com.chuanke.ckfamily.dao.finance.model.RedPacket;
import com.chuanke.ckfamily.dao.finance.model.RedPacketRule;
import com.chuanke.ckfamily.dao.social.model.Family;
import com.chuanke.ckfamily.dao.user.model.User;
import com.chuanke.ckfamily.service.base.FailCode;
import com.chuanke.ckfamily.service.base.FamilyValidator;
import com.chuanke.ckfamily.service.common.CacheService;
import com.chuanke.ckfamily.service.common.RSAService;
import com.chuanke.ckfamily.service.common.TipsService;
import com.chuanke.ckfamily.service.common.WyService;
import com.chuanke.ckfamily.service.common.exception.LockException;
import com.chuanke.ckfamily.service.common.impl.RedisLockUtil;
import com.chuanke.ckfamily.service.common.model.RSAKey;
import com.chuanke.ckfamily.service.constant.*;
import com.chuanke.ckfamily.service.exception.CFException;
import com.chuanke.ckfamily.service.finance.*;
import com.chuanke.ckfamily.service.finance.impl.thread.CreateRedpacketThread;
import com.chuanke.ckfamily.service.finance.vo.RedPacketRecod;
import com.chuanke.ckfamily.service.social.FamilyService;
import com.chuanke.ckfamily.service.user.UserService;
import com.chuanke.ckfamily.service.util.CkServerUtil;
import com.chuanke.ckfamily.service.util.RedpacketUtil;
import com.chuanke.ckfamily.service.util.model.UserBalance;
import com.weijie.core.BaseBiz;

import freemarker.template.TemplateException;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.access.method.P;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@Transactional(rollbackFor = Exception.class)
public class RedPacketServiceImpl extends BaseBiz<RedPacket, RedPacketDao> implements RedPacketService {

	private static final Logger log = LoggerFactory.getLogger(RedPacketServiceImpl.class);

	public static final String DEFAULT_NOTE = "齐心协力，共享收益";

	@Autowired
	private FamilyService familyService;

	@Autowired
	private UserService userService;

	@Autowired
	private CacheService cacheService;

	@Autowired
	private RedPacketRuleService redPacketRuleService;

	@Autowired
	private WyService wyService;

	@Autowired
	private BillService billService;

	@Autowired
	private RSAService rasService;

	@Autowired
	private PacketFinaceService packetFinaceService;

	@Autowired
	private GainPacketDao gainPacketDao;

	@Autowired
	private GainPacketService gainPacketService;

	@Autowired
	private TipsService tipsService;

	/**
	 * 定时任务，每分钟检查红包是否超时
	 *
	 */
	@Scheduled(cron = "0 * * * * ?")
	@Override
	public void autoCheckRedpacket() {
		log.debug("autoCheckRedpacket =================> ");
		Calendar closeTime = GregorianCalendar.getInstance();
		closeTime.add(Calendar.DAY_OF_YEAR, -1);
		List<RedPacket> redPacketList = baseDao.findNeedClosed(new Timestamp(closeTime.getTimeInMillis()));

		if (redPacketList != null && redPacketList.size() > 0) {
			for (RedPacket redPacket : redPacketList) {
				closeRedpacket(redPacket);
			}
		}

		Calendar cleanTime = GregorianCalendar.getInstance();
		cleanTime.add(Calendar.DAY_OF_YEAR, -1);
		List<RedPacket> cleanPacketList = baseDao.findNeedClosed(new Timestamp(cleanTime.getTimeInMillis()));

		if (cleanPacketList != null && cleanPacketList.size() > 0) {
			for (RedPacket cleanItem : cleanPacketList) {
				cleanRedpacket(cleanItem);
			}
		}
	}

	private void closeRedpacket(RedPacket redPacket) {
		RedisLockUtil lockUtil = null;
		try {
			lockUtil = cacheService.getLock(CacheService.KEY_PREFIX.packet_.name() + redPacket.getId());
			redPacket = findById(redPacket.getId());
			if (redPacket.getStatus().equals(RedPacketStatus.CLOSED.getStatus())) {
				return;
			}

			redPacket.setStatus(RedPacketStatus.CLOSED.getStatus());
			update(redPacket);

			Long backMoney = redPacket.getLeft_money();
			if (backMoney != null && backMoney.longValue() > 0) {
				if (redPacket.getType().intValue() == RedPacketType.FAMILY.getType()) {
					try {
						packetFinaceService.backFamilyPacket(redPacket.getFid(), redPacket.getId(), backMoney);
					} catch (CFException e) {
						e.printStackTrace();
					}

				} else {
					Family family = familyService.findById(redPacket.getFid());
					Long ownerId = family.getOwner_user_id();
					billService.addBill(ownerId, backMoney, BillType.PACKEBACK.getType(), null, "族长红包退回");

				}
			}

		} catch (Exception e) {
			e.printStackTrace();

		} finally {
			if (lockUtil != null) {
				lockUtil.unlock();
			}
			cleanRedpacket(redPacket);

		}
	}

	private void cleanRedpacket(RedPacket redPacket) {
		cacheService.delete(CacheService.KEY_PREFIX.packet_.name() + redPacket.getId());
		cacheService.delete(CacheService.KEY_PREFIX.packet_empty_.name() + redPacket.getId());
		Set<String> keySet = cacheService.getKeysByPrex(CacheService.KEY_PREFIX.packetPickup_.name() + redPacket.getId() + "_");
		if (keySet != null && keySet.size() > 0) {
			Iterator<String> keyIterator = keySet.iterator();
			while (keyIterator.hasNext()) {
				cacheService.delete(keyIterator.next());
			}
		}
	}

	@Override
	public void saveBatch(List<RedPacket> entities) {
		super.saveBatch(entities);
	}

	@Override
	public RedPacketRule createFamilyPackate(Long uid) throws CFException, IOException, TemplateException {
		if (!checkUserType(uid)) {
			throw new CFException(FailCode.APP, "该用户不是族长");
		}

		Family f = familyService.getByOwner(uid);
		RedPacketRule rpr = redPacketRuleService.getRedPacketRule(f.getBalance());
		if (rpr == null || f.getBalance() < rpr.getPackatemoney() * 100) {
			throw new CFException(FailCode.APP, tipsService.getSendRedpackage(f.getBalance()));
		}

		return rpr;
	}

	@Override
	public void sendFamilyPacket(Long uid, Long ruleId, String note) throws CFException {
		if (!checkUserType(uid)) {
			throw new CFException(FailCode.APP, "该用户不是族长");
		}

		RedPacketRule rule = redPacketRuleService.findById(ruleId);
		Long money = rule.getPackatemoney() * 100;

		Family f = null;
		RedisLockUtil lockUtil = null;
		try {
			lockUtil = cacheService.getLock(CacheService.KEY_PREFIX.packetSend_.name() + uid);
			f = familyService.getByOwner(uid);

			if (f.getBalance() < rule.getMinmoney() * 100 || f.getBalance() < money) {
				throw new CFException(FailCode.APP, tipsService.getSendRedpackage(f.getBalance()));
			}

			packetFinaceService.sendFamilyPacket(f.getId(), ruleId, money);
			try {
				RedPacket redPacket = addNewPacket(uid, f.getId(), RedPacketType.FAMILY.getType(), money, rule.getPackatenum().intValue(), note);
				sendRedpacket(userService.findById(uid), f, redPacket);

			} catch (Exception e) {
				packetFinaceService.backFamilyPacket(f.getId(), null, money);

			}

		} catch (Exception e) {
			e.printStackTrace();

		} finally {
			if (lockUtil != null) {
				lockUtil.unlock();
			}

		}
	}

	@Override
	public String getHeaderBalance(Long uid) throws CFException {
		if (uid == null) {
			throw new CFException(FailCode.APP, "参数为空");
		}

		UserBalance balance = CkServerUtil.loadBalance(uid);
		if (balance == null) {
			throw new CFException(FailCode.APP, "传客服务器返回用户余额错误");
		}

		return CkUtil.formatMoney(balance.getBalance());
	}

	@Override
	public RedPacket createUserPackate(Long uid) throws CFException {
		if (!checkUserType(uid)) {
			throw new CFException(FailCode.APP, "该用户不是族长");
		}

		RedPacket rp = new RedPacket();
		rp.setP_money(1l); // 红包总金额
		rp.setAll_total(10); // 总份额
		rp.setRnote(DEFAULT_NOTE);

		return rp;
	}

	@Override
	public void sendUserPacket(Long uid, Long money, int num, String note) throws CFException, LockException, UnsupportedEncodingException {
		if (!checkUserType(uid)) {
			throw new CFException(FailCode.APP, "该用户不是族长");
		}

		if (num < 10) {
			throw new CFException(FailCode.PARAMS_ERROR, "红包不能少于10个");
		}

		if (money < num) {
			throw new CFException(FailCode.REDPACKET_MONEYNO);
		}

		if (CkServerUtil.charge(uid, money, cacheService)) {
			RedisLockUtil lockUtil = null;
			try {
				lockUtil = cacheService.getLock(CacheService.KEY_PREFIX.packetSend_.name() + uid);
				Family family = familyService.getByOwner(uid);
				RedPacket redPacket = addNewPacket(uid, family.getId(), RedPacketType.PERSONAL.getType(), money, num, note);
				sendRedpacket(userService.findById(uid), family, redPacket);

			} catch (Exception e) {
				billService.addBill(uid, money, BillType.PACKEBACK.getType(), null, "发红包失败，退回金额");
				throw e;
			} finally {
				if (lockUtil != null) {
					lockUtil.unlock();
				}
			}

		} else {
			throw new CFException(FailCode.REDPACKET_LEDDERMONEYLOST);

		}
	}

	@Override
	public List<RedPacketRecod> findHistory(Long uid, Integer type, Integer p) throws CFException {
		Family family = familyService.getByOwner(uid);
		if (family == null) {
			throw new CFException(FailCode.PARAMS_ERROR, "无权查看");
		}

		Map<String, Object> param = new HashMap<>(4);
		param.put("fid", family.getId());
		if (type != null) {
			param.put("type", type.intValue());
		}
		param.put("start", p * 30);
		param.put("length", 30);

		List<RedPacket> redPacketList = baseDao.findHistory(param);
		List<RedPacketRecod> recodList = new ArrayList<>(redPacketList.size());
		for (RedPacket redpacket : redPacketList) {
			RedPacketRecod record = new RedPacketRecod();
			record.setPid(redpacket.getId());
			record.setType(RedPacketType.getTypeTitle(redpacket.getType()));
			record.setTime(DateFormatUtils.format(redpacket.getCreate_time(), "MM-dd"));
			if (redpacket.getStatus().intValue() == RedPacketStatus.NORMAL.getStatus()) {
				record.setStatus("已领" + (redpacket.getAll_total() - redpacket.getLeft_total()) + "/" + redpacket.getAll_total());

			} else if (redpacket.getStatus().intValue() == RedPacketStatus.EMPTY.getStatus()) {
				record.setStatus("已领完");

			} else {
				record.setStatus("退款" + CkUtil.formatMoney(redpacket.getLeft_money()));
			}

			record.setFamily("");
			record.setMoney(CkUtil.formatMoney(redpacket.getP_money()));
			recodList.add(record);
		}

		log.debug("findHistry===========>uid={}, type={}, p={}, result={}", uid, type, p, JSON.toJSONString(recodList));
		return recodList;
	}

	@Override
	public Long countHistory(Long uid, Integer type) throws CFException {
		Family family = familyService.getByOwner(uid);
		if (family == null) {
			throw new CFException(FailCode.PARAMS_ERROR, "无权查看");
		}

		Map<String, Object> param = new HashMap<>(2);
		param.put("fid", family.getId());
		if (type != null) {
			param.put("type", type);
		}

		log.debug("count ==========>{}", type);
		return baseDao.countHistory(param);
	}

	@Override
	public String sumHistory(Long uid, Integer type) throws CFException {
		Family family = familyService.getByOwner(uid);
		if (family == null) {
			throw new CFException(FailCode.PARAMS_ERROR, "无权查看");
		}

		Map<String, Object> param = new HashMap<>(2);
		param.put("fid", family.getId());
		if (type != null) {
			param.put("type", type);
		}

		Long money = baseDao.sumHistory(param);
		return CkUtil.formatMoney(money);
	}

	/**
	 * 	创建红包
	 *
	 */
	private RedPacket addNewPacket(Long uid, Long fid, int type, Long money, int num, String note) throws CFException {
		if (StringUtils.isEmpty(note) || note.trim().length() > 30) {
			throw new CFException(FailCode.APP, "红包主题不能大于30个字");
		}

		if (uid == null || fid == null || money == null) {
			throw new CFException(FailCode.APP, "参数为空");
		}

		RedPacket rp = new RedPacket();
		rp.setUid(uid);
		rp.setFid(fid);
		rp.setP_money(money); // 红包总金额
		rp.setLeft_money(money); // 红包剩余金额
		rp.setAll_total(num); // 总份额
		rp.setLeft_total(num); // 剩余份额
		rp.setType(type); // 红包的类型 。。。
		rp.setRnote(note);
		return rp;
	}

	/**
	 * 发送红包，预拆红包
	 *
	 * @param user
	 * @param family
	 * @param redPacket
	 * @throws CFException
	 */
	private void sendRedpacket(User user, Family family, RedPacket redPacket) throws CFException {
		redPacket.setStatus(RedPacketStatus.NORMAL.getStatus()); // 红包状态 创建时为0
																	// 正常
		redPacket.setCreate_time(new Timestamp(System.currentTimeMillis()));
		save(redPacket);

		createUserPacket(redPacket);

		RSAKey rsaKey = rasService.getRSAKeyMsg();
		JSONObject bodyJson = new JSONObject();
		bodyJson.put("title", CkUtil.getRedpacketTitle(redPacket.getRnote()));
		bodyJson.put("type", MsgEunm.redpacket_send.getMsgtype());
		bodyJson.put("key", rsaKey.getPublicKey());
		bodyJson.put("sender_id", user.getId());
		if (redPacket.getType().intValue() == RedPacketType.FAMILY.getType()) {
			bodyJson.put("sender_pic", family.getFicon());

		} else {
			bodyJson.put("sender_pic", user.getPic());

		}

		JSONObject redpacket = new JSONObject(2);
		redpacket.put("money", CkUtil.formatMoney(redPacket.getP_money()));
		redpacket.put("num", redPacket.getAll_total());
		redpacket.put("pid", redPacket.getId());
		redpacket.put("type", redPacket.getType());

		String extStr = rasService.encryptByPrivateKey(redpacket.toString(), rsaKey.getKeyPair().getPrivate());
		bodyJson.put("redpacket", extStr);

		wyService.sendMsgForHongBao(user.getUser_no(), family.getTid(), bodyJson);
	}

	private void createUserPacket(RedPacket redPacket) {
		int num = redPacket.getLeft_total();
		Long money = redPacket.getLeft_money();
		
		List<Long> list = new ArrayList<>();
		for (int i = 0; i < redPacket.getLeft_total(); i++) {
			Long itemMoney = RedpacketUtil.getRandonMoney(num, money);
			list.add(itemMoney);
			
			num--;
			money -= itemMoney;
		}
		//批量保存到缓存
		if(list!=null && !list.isEmpty()){
			Long[] array = list.toArray(new Long[list.size()]);
			cacheService.leftPushAll(CacheService.KEY_PREFIX.packet_money_queue_.name() + redPacket.getId(), array);
		}
		
	}

	// 判断用户是否已经有家族，并且是否为族长 是true 否false
	private boolean checkUserType(Long uid) throws CFException {
		if (uid == null) {
			throw new CFException(FailCode.APP, "参数为空");
		}

		Family f = familyService.getByOwner(uid);
		if (f == null) {
			throw new CFException(FailCode.APP, "家族不存在");
		}

		return f.getOwner_user_id().longValue() == uid;
	}

	@Override
	public RedPacket findById(Object id) {
		RedPacket redPacket = (RedPacket) cacheService.get(CacheService.KEY_PREFIX.packet_.name() + id);
		if (redPacket == null) {
			redPacket = super.findById(id);
		}

		return redPacket;
	}

	public RedPacket findByIdUpdate(Long id) {
		RedPacket redPacket = (RedPacket) cacheService.get(CacheService.KEY_PREFIX.packet_.name() + id);
		if (redPacket == null) {
			redPacket = baseDao.findByIdForUpdate(id);
			cacheService.set(CacheService.KEY_PREFIX.packet_.name() + id, redPacket);
		}
		return redPacket;
	}

	@Override
	public void update(RedPacket redPacket) {
		super.update(redPacket);
		cacheService.set(CacheService.KEY_PREFIX.packet_.name() + redPacket.getId(), redPacket);
	}

	public void save(RedPacket redPacket) {
		super.save(redPacket);
		cacheService.set(CacheService.KEY_PREFIX.packet_.name() + redPacket.getId(), redPacket);
	}

	@Override
	public void sendMemberPacket(Long uid, String tid) throws CFException {
		Family family = familyService.findFamilyByUserId(uid);
		FamilyValidator.validateFamilyTid(family, tid);
	}

	@Override
	public void updateFalsePacket2() {
		List<Map<String, Object>> list = baseDao.findFalsePacket();
		log.info("all num ===========>" + list.size());
		
		for (Map<String, Object> map : list) {
			// 查找 红包已经领取完了并且领取金额和总金额相等的红包，将红包的 剩余金额和剩余个数修正
			if (map.get("t").toString().equals(map.get("all_total").toString()) && map.get("p").toString().equals(map.get("p_money").toString())) {
				log.info("need update packet ---------------> map={}", map);
				RedPacket redPacket = findById(map.get("id"));

				redPacket.setLeft_money(0L);
				redPacket.setLeft_total(0);
				redPacket.setStatus(2);
				update(redPacket);

			}
			
			if (Long.parseLong(map.get("p").toString()) < Long.parseLong(map.get("p_money").toString())) {
				log.info("need change packet money ---------------> map={}", map);
				RedPacket rp = findById(map.get("id"));

				rp.setLeft_money(Long.parseLong(map.get("p_money").toString()) - Long.parseLong(map.get("p").toString()));
				rp.setLeft_total(Integer.parseInt(map.get("all_total").toString()) - Integer.parseInt(map.get("t").toString()));
				update(rp);
			}
		}
	}

	@Override
	public void updateFalsePacket3(String pid) {
//		List<Map<String, Object>> list = baseDao.findFalsePacket();
//		for(Map<String, Object> map : list){
//			String id = map.get("id").toString(); // 红包id
//			// 检查缓存中存在的红包和 数据库 gain红包是否一致
//			Integer size = cacheService.getByPrexSize(CacheService.KEY_PREFIX.packetPickup_.name() + id + "_");
//			if(size.equals(map.get("t"))){
//
//			}
//
//		}

		Set<String> keySet = cacheService.getKeysByPrex(CacheService.KEY_PREFIX.packetPickup_.name() + pid + "_");
		List<Bill> billsList = new ArrayList<>(10);
		System.out.println(keySet.toString()+"======================> keyset");
		if (keySet != null && keySet.size() > 0) {
			Iterator<String> keyIterator = keySet.iterator();
			while (keyIterator.hasNext()) {
				GainPacket gainPacket = (GainPacket) cacheService.get(keyIterator.next());
				// 将缓存中的红包  和数据库进行匹配， 若不一致 则要添加此 gainpacket
				try {
					Map<String, Object> param = new HashMap<>(2);
					param.put("uid", gainPacket.getUid());
					param.put("pid", gainPacket.getPid());
					log.info(" map ====================> param={}",param);
					GainPacket gainPacket1 = gainPacketDao.findGainRecord(param);
					// 返回为空  则表示 缓存中存在该用户获得了红包  而数据库中没有同步
					if(gainPacket1 == null){
						// 需要将该红包同步数据库
						gainPacketService.save(gainPacket);

						RedPacket packet = findById(gainPacket.getPid());
						Bill bill = new Bill();
						bill.setMoney(gainPacket.getPrice());
						bill.setUid(gainPacket.getUid());
						bill.setCreate_time(gainPacket.getGain_time());
						bill.setOptype(packet.getType().intValue() == RedPacketType.FAMILY.getType()
								? BillType.FAMILYPACKET.getType() : BillType.HEADPACKET.getType());
						bill.setFlag(0);
						bill.setFnote("领取红包");

						log.info(" packet and bill ===============> gainPacket={},bill={}",gainPacket,bill);
						billsList.add(bill);

						// 更新红包表
						Long money = packet.getLeft_money();
						Integer toatl = packet.getLeft_total();
						packet.setLeft_money(money - gainPacket.getPrice());
						packet.setLeft_total(toatl - 1);
						if(packet.getLeft_money() <= 0){
							packet.setStatus(2);
						}
						update(packet);

					}

				} catch (Exception e) {

					e.printStackTrace();
				}
			}
		}

		if(billsList != null && billsList.size()>0){
			billService.addBills(billsList);
		}

	}

	@Override
	public void updateFalsePacket() {
		// 将10.06 下午五点后的发布的红包都找出来 ， 遍历红包 和 获得的红包
		List<Map<String, Object>> list = baseDao.findFalsePacket();
		List<Map<String, Object>> rl = new ArrayList<>(); // 将需要退款的放进来，日志打印此list

		for (Map<String, Object> map : list) {
			// 查找 红包已经领取完了并且领取金额和总金额相等的红包，将红包的 剩余金额和剩余个数修正
			if (map.get("t").toString().equals(map.get("all_total").toString()) && map.get("p").toString().equals(map.get("p_money").toString())) {
				log.info("need update packet ---------------> map={}", map);
				RedPacket redPacket = findById(map.get("id"));

				redPacket.setLeft_money(0L);
				redPacket.setLeft_total(0);
				redPacket.setStatus(2);
				update(redPacket);

			}

			// 将那些红包没有领取完的 红包剩余个数和剩余金额修正
			if (Long.parseLong(map.get("p").toString()) < Long.parseLong(map.get("p_money").toString())) {
				log.info("need change packet money ---------------> map={}", map);
				RedPacket rp = findById(map.get("id"));

				rp.setLeft_money(Long.parseLong(map.get("p_money").toString()) - Long.parseLong(map.get("p").toString()));
				rp.setLeft_total(Integer.parseInt(map.get("all_total").toString()) - Integer.parseInt(map.get("t").toString()));
				update(rp);

				// 查找红包领取完了，但是 获得红包的总金额 小于 红包金额的 将钱退回对应的家族 和个人（针对昨晚十点四十以后的错误数据）
				// 判断依据是 红包的领取个数和总个数相等，但是领取的金额小于 总金额
				if (map.get("t").toString().equals(map.get("all_total").toString())) {

					Long leftPrice = Long.parseLong(map.get("p_money").toString()) - Long.parseLong(map.get("p").toString());
					map.put("leftPrice", leftPrice); // 将退回的钱记录
					rl.add(map);

					if (map.get("type").toString().equals("1")) { // 家族红包
						Family family = familyService.findById(map.get("fid"));
						Long balance = family.getBalance();
						family.setBalance(balance + leftPrice);
						familyService.update(family);

					} else { // 调用传客接口增加余额
						Bill bill = new Bill();
						bill.setUid(Long.parseLong(map.get("uid").toString()));
						bill.setMoney(leftPrice);
						bill.setOptype(-1);
						bill.setFlag(BillStatus.UNSUBMIT.getStatus());
						bill.setCreate_time(new Timestamp(System.currentTimeMillis()));
						bill.setFnote(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
						try {
							CkServerUtil.uploadUserMoney(bill, cacheService);

						} catch (Exception e) {
							log.info("回款失败");
							e.printStackTrace();
						}

					}

				}

			}

		}

		log.info("back money packet==================> rl={}", rl);
	}
}